﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

using System.Collections.Specialized;
using Enterprise.Data.Repository;
using System.Data.Objects;
using System.Data;

namespace Enterprise.Data.Manager
{
    public class DataRepository : IDataRepository
    {

        private IUnitOfWork Uwork = IoC.Resolve<IUnitOfWork>();
        /// <summary>
        /// Updates entity object
        /// </summary>
        /// <param name="entity"></param>
        public void Update<TEntity>(TEntity entity) where TEntity : class
        {
          
            this.Uwork.objectContext.CreateObjectSet<TEntity>().Attach(entity);
            this.Uwork.objectContext.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
            this.Uwork.SaveChanges();
        }
        /// <summary>
        /// Provides explicit loading of object properties
        /// </summary>
        public void LoadProperty<TEntity>(TEntity entity, Expression<Func<TEntity, object>> selector)
        {
            this.Uwork.objectContext.LoadProperty<TEntity>(entity, selector);
        }
        /// <summary>
        /// Returns all entities for a given type
        /// </summary>
        /// <returns>All entities</returns>
        public List<TEntity> GetAll<TEntity>(params string[] NavigationChildren) where TEntity : class
        {
            if (NavigationChildren == null || NavigationChildren.Length == 0)
            {
                return this.Uwork.objectContext.CreateObjectSet<TEntity>().ToList();//(Objects.SingleOrDefault(e => e.ID == id);
            }
            ObjectQuery<TEntity> entity = this.Uwork.objectContext.CreateObjectSet<TEntity>();
            return NavigationChildren.Aggregate<string, ObjectQuery<TEntity>>(entity, (current, child) => current.Include(child)).ToList(); ;

        }
        /// <summary>
        /// Returns  entities for a given type based on the query string
        /// </summary>
        /// <param name="Query"></param>
        /// <param name="Paramaters"></param>
        /// <returns></returns>

        public List<TEntity> Search<TEntity>(string Query, params string[] NavigationChildren) where TEntity : class
        {
            if (NavigationChildren == null || NavigationChildren.Length == 0)
            {
                return this.Uwork.objectContext.CreateObjectSet<TEntity>().Where(Query).ToList();
            }
            ObjectQuery<TEntity> entity = this.Uwork.objectContext.CreateObjectSet<TEntity>();
            return NavigationChildren.Aggregate<string, ObjectQuery<TEntity>>(entity, (current, child) => current.Include(child)).Where(Query).ToList();
        }

        /// <summary>
        /// Add entity to the repository
        /// </summary>
        /// <param name="entity">the entity to add</param>
        /// <returns>The added entity</returns>
        public void Add<TEntity>(TEntity entity) where TEntity : class
        {
            this.Uwork.objectContext.CreateObjectSet<TEntity>().AddObject(entity);
            this.Uwork.SaveChanges();
        }


        /// <summary>
        /// Save entity to the repository, it could be insert or update
        /// </summary>
        /// <param name="entity">the entity to Save</param>
        /// <returns>Saved entity</returns>
        public void Save<TEntity>(TEntity entity, int KeyValue) where TEntity : class
        {
            if (KeyValue > 0)
                Update<TEntity>(entity);
            else
                Add<TEntity>(entity);

        }



        /// <summary>
        /// Mark entity to be deleted within the repository
        /// </summary>
        /// <param name="entity">The entity to delete</param>
        public void Delete<TEntity>(TEntity entity) where TEntity : class
        {

            this.Uwork.objectContext.CreateObjectSet<TEntity>().DeleteObject(entity);
            this.Uwork.SaveChanges();
        }

        /// <summary>
        /// Mark entity to be deleted within the repository
        /// </summary>    
        /// <param name="criteria"> Predicate used to identifay entities to be deleted</param>

        public void Delete<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
        {

            foreach (TEntity entity in this.Uwork.objectContext.CreateObjectSet<TEntity>().Where(criteria).ToList())
                Delete<TEntity>(entity);

        }

        /// <summary>
        /// Returns  entities based on passed query predicate
        /// </summary>
        /// <typeparam name="TEntity"> Entity type to return</typeparam>
        /// <param name="criteria">Predicate criteria for search</param>
        /// <param name="NavigationChildren"> Navigation child to be included as a part of the result set</param>
        /// <returns></returns>
        public List<TEntity> Search<TEntity>(Expression<Func<TEntity, bool>> criteria, params string[] NavigationChildren) where TEntity : class
        {
            if (NavigationChildren == null || NavigationChildren.Length == 0)
            {
                return this.Uwork.objectContext.CreateObjectSet<TEntity>().Where(criteria).ToList();
            }
            ObjectQuery<TEntity> entity = this.Uwork.objectContext.CreateObjectSet<TEntity>();
            return NavigationChildren.Aggregate<string, ObjectQuery<TEntity>>(entity, (current, child) => current.Include(child)).Where(criteria).ToList();
        }

        /// <summary>
        /// Returns the first of default result bassed on the predicate.
        /// </summary>
        /// <typeparam name="TEntity">Entity type to return</typeparam>
        /// <param name="criteria">Predicate criteria for search</param>
        /// <param name="NavigationChildren">Navigation child to be included as a part of the result set.</param>
        /// <returns></returns>
        public TEntity First<TEntity>(Expression<Func<TEntity, bool>> criteria, params string[] NavigationChildren) where TEntity : class
        {
            if (NavigationChildren == null || NavigationChildren.Length == 0)
            {
                return this.Uwork.objectContext.CreateObjectSet<TEntity>().Where(criteria).FirstOrDefault();
            }
            ObjectQuery<TEntity> entity = this.Uwork.objectContext.CreateObjectSet<TEntity>();
            return NavigationChildren.Aggregate<string, ObjectQuery<TEntity>>(entity, (current, child) => current.Include(child)).Where(criteria).FirstOrDefault();
        }


    }
}
